import { Context, Middleware, Next } from "koa";
import KoaJwt from "koa-jwt";
import jwt from "jsonwebtoken";

// #region  definations
export enum tokenType {
  access,
  refresh,
}

export interface ICtxUserInfo {
  type: tokenType;
}

export interface AuthError extends Error {
  status: number;
  code: string;
}

export interface ITokenOptions {
  secret: string;
  expire: string;
  refresh: string;
}
// #endregion

// #region auth middlewares
export function jwtAuth(secret: string): Middleware {
  return KoaJwt({ secret });
}

export async function requestAccessToken(ctx: Context, next: Next) {
  if ((ctx.state.user as ICtxUserInfo).type === tokenType.refresh) {
    const err = new Error() as AuthError;
    err.status = 400;
    err.code = "Require Access token";
    err.message = "Access Token is required.";
    throw err;
  }
  await next();
}

export async function requestRefreshToken(ctx: Context, next: Next) {
  if ((ctx.state.user as ICtxUserInfo).type === tokenType.access) {
    const err = new Error() as AuthError;
    err.status = 400;
    err.code = "Require Refresh token";
    err.message = "Refresh Token is required.";
    throw err;
  }
  await next();
}
// #endregion

// #region auth grant
export function getToken(options: ITokenOptions): Middleware {
  return async function (ctx: Context, next: Next) {
    const id = ctx.state.id || ctx.state.user.id;
    var token = jwt.sign({ id, type: "access" }, options.secret, {
      expiresIn: options.expire,
    });
    var refreshToken = jwt.sign({ id, type: "refresh" }, options.secret, {
      expiresIn: options.refresh,
    });
    ctx.body = {
      success: true,
      message: "success",
      data: { id, token, refreshToken },
    };
  };
}
// #endregion
